package mk.jug.academy.controller;

import java.io.IOException;
import java.text.MessageFormat;
import java.util.Map;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import mk.jug.academy.model.Lecturer;
import mk.jug.academy.model.Sponsor;
import mk.jug.academy.model.Student;
import mk.jug.academy.model.Supporter;
import mk.jug.academy.model.UserType;
import mk.jug.academy.service.LecturerService;
import mk.jug.academy.service.SponsorService;
import mk.jug.academy.service.StudentService;
import mk.jug.academy.service.SupporterService;
import mk.jug.academy.util.Utils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.encoding.PasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import com.google.appengine.api.blobstore.BlobKey;
import com.google.appengine.api.blobstore.BlobstoreService;
import com.google.appengine.api.blobstore.BlobstoreServiceFactory;

/**
 * View layer coordinator for the registration process.
 *
 * @author Ice.Penov
 */
@Controller
public class RegistrationController {
	
	public static final String REGISTER = "register";
	public static final String REGISTER_SUPPORTER = "register.supporter";
	public static final String REGISTER_CONFIRM = "register.confirm";
	public static final String REGISTER_STUDENT = "register.student";
	public static final String REGISTER_LECTURER = "register.lecturer";
	public static final String REGISTER_SPONSOR = "register.sponsor";
	
	public static final String INVALID_REQUEST = "invalid.request";	
	public static final String TECH_ERROR = "technical.error";
	
	private static final Logger LOG = Logger.getLogger(RegistrationController.class.getName());
	
	@Autowired private SupporterService supporterService;
	@Autowired private LecturerService lecturerService;
	@Autowired private StudentService studentService;
	@Autowired private SponsorService sponsorService;
	
	@Autowired private PasswordEncoder passwordEncoder;
	@Autowired private Properties properties;
	
	private BlobstoreService blobstoreService = BlobstoreServiceFactory.getBlobstoreService();	
	
	@RequestMapping("/register.do")
	public String register(ModelMap map, @RequestParam(required = false) String type) {
		boolean isSet = false;
		if (type != null) {
			if (type.equalsIgnoreCase(UserType.SUPPORTER.toString())
					|| type.equalsIgnoreCase(UserType.STUDENT.toString())
					|| type.equalsIgnoreCase(UserType.LECTURER.toString())
					|| type.equalsIgnoreCase(UserType.SPONSOR.toString())) {
				map.addAttribute("type", type.toLowerCase());
				isSet = true;
			}
		}
		if (!isSet) {
			map.addAttribute("type", UserType.SUPPORTER.toString().toLowerCase());
		}
		return REGISTER;
	}
	
	@RequestMapping(value = "/registerUser.do")
	public String registerUser(ModelMap map, @RequestParam String type) {
		if (type.equalsIgnoreCase(UserType.SUPPORTER.toString())) {
			map.addAttribute(type, new Supporter());
			map.addAttribute("formURL", blobstoreService.createUploadUrl("/registerSupporter.do"));
			return REGISTER_SUPPORTER;
		} else if (type.equalsIgnoreCase(UserType.STUDENT.toString())) {
			map.addAttribute(type, new Student());
			map.addAttribute("formURL", blobstoreService.createUploadUrl("/registerStudent.do"));
			return REGISTER_STUDENT;
		} else if (type.equalsIgnoreCase(UserType.LECTURER.toString())) {
			map.addAttribute(type, new Lecturer());
			map.addAttribute("formURL", blobstoreService.createUploadUrl("/registerLecturer.do"));
			return REGISTER_LECTURER;
		} else if (type.equalsIgnoreCase(UserType.SPONSOR.toString())) {
			map.addAttribute(type, new Sponsor());
			map.addAttribute("formURL", blobstoreService.createUploadUrl("/registerSponsor.do"));
			return REGISTER_SPONSOR;
		} else {
			return INVALID_REQUEST;
		}
	}
	
	@RequestMapping(value = "/registerSupporter.do", method = RequestMethod.POST)
	public String registerSupporter(HttpServletRequest request, @ModelAttribute("supporter") Supporter supporter, ModelMap map) {
		Map<String, BlobKey> blobs = blobstoreService.getUploadedBlobs(request);
		BlobKey key = blobs.get("picture");
		supporter.setImageKey(key.getKeyString());
		supporter.setPassword(passwordEncoder.encodePassword(supporter.getPassword(), supporter.getSalt()));
		supporterService.signUpSupporter(supporter);
		String subject = properties.getProperty("registration.supporter.email.subject");
		String body = MessageFormat.format(properties.getProperty("registration.supporter.email.body"), new Object[] {supporter.getDisplayName()});
		Utils.sendMail(new String[] {supporter.getEmail(), supporter.getDisplayName()}, subject, body);
		return "redirect:/registrationConfirmation.do";
	}
	
	@RequestMapping(value = "/registerStudent.do", method = RequestMethod.POST)
	public String registerStudent(HttpServletRequest request, @ModelAttribute("student") Student student) {
		Map<String, BlobKey> blobs = blobstoreService.getUploadedBlobs(request);
		BlobKey key = blobs.get("picture");
		student.setImageKey(key.getKeyString());
		student.setPassword(passwordEncoder.encodePassword(student.getPassword(), student.getSalt()));
		studentService.registerStudent(student);
		String subject = properties.getProperty("registration.student.email.subject");
		String body = MessageFormat.format(properties.getProperty("registration.student.email.body"), new Object[] {student.getDisplayName()});
		Utils.sendMail(new String[] {student.getEmail(), student.getDisplayName()}, subject, body);
		return "redirect:/registrationConfirmation.do";
	}
	
	@RequestMapping(value = "/registerLecturer.do", method = RequestMethod.POST)
	public String registerLecturer(HttpServletRequest request, @ModelAttribute("lecturer") Lecturer lecturer) {
		Map<String, BlobKey> blobs = blobstoreService.getUploadedBlobs(request);
		BlobKey key = blobs.get("picture");				
		lecturer.setImageKey(key.getKeyString());
		lecturer.setPassword(passwordEncoder.encodePassword(lecturer.getPassword(), lecturer.getSalt()));
		lecturerService.registerLecturer(lecturer);
		String subject = properties.getProperty("registration.lecturer.email.subject");
		String body = MessageFormat.format(properties.getProperty("registration.lecturer.email.body"), new Object[] {lecturer.getDisplayName()});
		Utils.sendMail(new String[] {lecturer.getEmail(), lecturer.getDisplayName()}, subject, body);
		return "redirect:/registrationConfirmation.do";
	}
	
	@RequestMapping(value = "/registerSponsor.do", method = RequestMethod.POST)
	public String registerSponsor(HttpServletRequest request, @ModelAttribute("sponsor") Sponsor sponsor) {
		Map<String, BlobKey> blobs = blobstoreService.getUploadedBlobs(request);
		BlobKey key = blobs.get("picture");
		sponsor.setImageKey(key.getKeyString());
		sponsor.setPassword(passwordEncoder.encodePassword(sponsor.getPassword(), sponsor.getSalt()));
		sponsorService.register(sponsor);
		String subject = properties.getProperty("registration.sponsor.email.subject");
		String body = MessageFormat.format(properties.getProperty("registration.sponsor.email.body"), new Object[] {sponsor.getDisplayName()});
		Utils.sendMail(new String[] {sponsor.getEmail(), sponsor.getDisplayName()}, subject, body);
		return "redirect:/registrationConfirmation.do";
	}
	
	@RequestMapping(value = "/registrationConfirmation.do")
	public String registrationConfirmation() {
		return REGISTER_CONFIRM;
	}
	
	@RequestMapping(value = "/validation/user/email.do")
	public String validateDuplicateEmail(@RequestParam(value = "email") String email, HttpServletResponse response) {
		try {
			if (existsForAnyUserType(email)) {
				response.getWriter().append("false");
			} else {
				response.getWriter().append("true");
			}
			return null;
		} catch (IOException e) {
			LOG.log(Level.SEVERE, "I/O Exception occured.", e);
			return TECH_ERROR;
		}
	}
	
	private boolean existsForAnyUserType(String email) {
		return supporterService.exists(email) || lecturerService.exists(email)
			|| sponsorService.exists(email) || studentService.exists(email);
	}

}